import Head from "./Head";
import Body from "./Body";
import { showHideChildren, sortHandler, addRemoveExpand,
    onResizeStart, onResizeMove, onResizeEnd, initColumns, updateScrollFixed, initData,
    observerSizeChange} from "./utils";
import Spin from "../Spin";
import { PropType, VNode, computed, defineComponent, onBeforeUnmount, onMounted, provide, reactive, ref, toRef, watch, watchEffect } from "vue";
import { PopoverProps } from "../Popover";
import Summary from "./Summary";

type TableProps = {
    columns: any[],
    data: any[],
    rowKey?: string,
    height?: number,
    border?: boolean,
    stripe?: boolean,
    highlight?: boolean,
    showHeader?: boolean,
    selectedRowKeys?: KeyType[],
    onRowSelect?: (row: any, preRow: any) => void,
    onRowChecked?: (row: any, checked: boolean) => void,
    onCheckedAll?: (rows: any[]) => void,
    onSort?: (column: any, sortType: any) => void,
    size?: 'small',
    spanMethod?: (data: any, column: any, index: number, columnIndex: number) => any,
    showSummary?: boolean,
    summaryMethod?: (columns: ColumnProps[], data: any[]) => any,
    loading?: boolean,
    loadingText?: string|VNode,
    title?: string | number | VNode,
    footer?: string | number | VNode,
    empty?: string | number | VNode,
    virtual?: boolean
}
// 表格存储
export type TableStore = {
    columns: ColumnProps[],
    columnsRows: any[],
    data: any[],
    showFixedLeft: boolean,
    showFixedRight: boolean,
    checkedAll: boolean | string,
    resizing: boolean,
    headerSize: any,
    summarySize: any,
    headerLeft: number,
    x: number,
    posX: number,
    startX: number,
    resizeId?: string
}

export type ColumnProps = {
    name?: string,
    title?: string | VNode,
    render?: (data: {value: any, column: any, record: any, index: number}) => any,
    type?: 'index'|'date'|'datetime'|'enum'|'checkbox'|'expand',
    width?: string,
    minWidth?: number,
    maxWidth?: number,
    _width?: number,
    resize?: boolean,
    sort?: boolean | 'custom',
    sortMethod?(a: any, b: any): number,
    sortType?: 'asc'|'desc'|'',
    fixed?: 'left'|'right',
    tree?: boolean,
    ellipsis?: boolean,
    tooltip?: boolean,
    tooltipAlign?: PopoverProps['align'],
    tooltipTheme?: PopoverProps['color'],
    tooltipMaxWidth?: number,
    tooltipStyle?: any,
    fixedLeftLast?: boolean,
    fixedRightFirst?: boolean,
    children?: ColumnProps[],
    _colspan?: number,
    _rowspan?: number,
    _parent?: ColumnProps,
    _level?: number,
    id: string,
    _index: number,
    // 触发更新使用
    _: string
    enum?: { [key: string]: string }
}

export const TableContextKey = Symbol('TableContextKey');

export default defineComponent({
    name: 'Table',
    props: {
        columns: {type: Array as PropType<TableProps['columns']>},
        data: {type: Array as PropType<TableProps['data']>},
        rowKey: {type: String as PropType<TableProps['rowKey']>, default: 'id'},
        height: {type: Number, default: undefined},
        border: {type: Boolean, default: false},
        stripe: {type: Boolean, default: false},
        highlight: {type: Boolean as PropType<TableProps['highlight']>},
        showHeader: {type: Boolean as PropType<TableProps['showHeader']>, default: true},
        selectedRowKeys: {type: Array as PropType<TableProps['selectedRowKeys']>, default: () => []},
        size: {type: String as PropType<TableProps['size']>},
        spanMethod: {type: Function as PropType<TableProps['spanMethod']>},
        showSummary: {type: Boolean as PropType<TableProps['showSummary']>},
        summaryMethod: {type: Function as PropType<TableProps['summaryMethod']>},
        loading: {type: Boolean as PropType<TableProps['loading']>},
        loadingText: {type: [String, Object] as PropType<TableProps['loadingText']>},
        title: {type: [String, Object] as PropType<TableProps['title']>},
        footer: {type: [String, Object] as PropType<TableProps['footer']>},
        empty: {type: [String, Object] as PropType<TableProps['empty']>},
        virtual: {type: Boolean as PropType<TableProps['virtual']>},
    },
    emits: ['rowSelect', 'rowChecked', 'checkedAll', 'sort', 'update:selectedRowKeys'],
    setup (props, {attrs, emit, expose}) {
        const classList = computed(() => ({
            'cm-table-wrap': true,
            'cm-table-border': props.border,
            'cm-table-stripe': props.stripe,
            'cm-table-small': props.size === 'small',
            'cm-table-with-title': props.title,
            'cm-table-with-footer': props.footer,
            'cm-table-with-summary': props.showSummary,
            'cm-table-resizing': store.resizing
        }));
        const {maxFixedLeft, minFixedRight} = initColumns(props.columns);
        const selectedRowKeys = ref(props.selectedRowKeys ?? []);
        let data: any[] = initData(props.data, props.rowKey);
        const wrap = ref();

        watch(() => props.selectedRowKeys, () => {
            selectedRowKeys.value = props.selectedRowKeys;
        });

        const store = reactive({
            columns: [],
            columnsRows: [],
            data: [],
            showFixedLeft: false,
            showFixedRight: true,
            checkedAll: false,
            resizing: false,
            x: 0,
            posX: 0,
            startX: 0,
            resizeId: undefined,
            headerSize: {
                with: 0,
                height: 48
            },
            summarySize: {
                with: 0,
                height: 0
            },
            headerLeft: 0,
        } as TableStore);

        // 传入的data变化
        watchEffect(() => {
            data = initData(props.data, props.rowKey);
            store.data = data;
            store.checkedAll = false;
        });

        // 传入的columns变化
        watchEffect(() => {
            const {columnsRows, calcColumns} = initColumns(props.columns);
            store.columns = calcColumns;
            store.columnsRows = columnsRows;
            store.showFixedLeft = false;
            store.showFixedRight = true;
        });

        // 滚动条滚动更新固定列
        const onScroll = (e: any) => {
            // updateScrollFixed(maxFixedLeft, minFixedRight, setStore, e);
        };

        // 行选高亮事件
        const onSelectRow = (row: any) => {
            const lastRow = store.data.find((item: any) => {
                return item._highlight;
            });
            if (lastRow) {
                lastRow._highlight = false;
            }
            store.data.forEach(item => {
                if (item.id === row.id) {
                    item._highlight = true;
                }
            });
            emit('rowSelect', row, lastRow);
        };

        // 选择框选择，并计算是否所有都选中，排除禁用行
        const onRowChecked = (row: any, checked: boolean) => {
            store.data.forEach(item => {
                if (item.id === row.id) {
                    item._checked = checked;
                }
            });
            let status: boolean | string = false;
            let checkedNum = 0;
            let total = 0;
            const checkedKeys: KeyType[] = [];
            store.data.forEach((item: any) => {
                if (!item._disabled) {
                    total++;
                }
                if (item._checked) {
                    checkedKeys.push(item.id);
                    checkedNum ++;
                    status = 'indeterminate';
                }
            });
            if (checkedNum >= total) {
                status = true;
            }

            if (checkedKeys.join(',') !== selectedRowKeys.value?.join(',')) {
                selectedRowKeys.value = checkedKeys;
                emit('update:selectedRowKeys', checkedKeys);
            }

            store.checkedAll = status;
            emit('rowChecked', row, checked);
        };

        watch(() => selectedRowKeys.value, (rowKeys) => {
            if (rowKeys && rowKeys.length > 0) {
                store.data.forEach(item => {
                    if (rowKeys.includes(item.id) && !item._checked) {
                        item._checked = true;
                    }
                });
            } else {
                store.data.filter(item => item._checked).forEach(item => {
                    item._checked = false;
                });
            }
            let status: boolean | string = false;
            let checkedNum = 0;
            let total = 0;
            store.data.forEach((item: any) => {
                if (!item._disabled) {
                    total++;
                }
                if (item._checked) {
                    checkedNum ++;
                    status = 'indeterminate';
                }
            });
            if (checkedNum >= total) {
                status = true;
            }

            store.checkedAll = status;
        }, { immediate: true });

        // 头部选择框选中事件,禁用的选择框不进行响应
        const onHeadChecked = (checked: boolean) => {
            store.checkedAll = checked;
            store.data.forEach(item => {
                if (checked && !item._disabled && !item._checked) {
                    item._checked = checked;
                }
                if (!checked && !item._disabled && item._checked) {
                    item._checked = checked;
                }
            });
            const checkedKeys: KeyType[] = [];
            const rows = store.data.filter(item => {
                if (item._checked) {
                    checkedKeys.push(item.id);
                }
                return item._checked;
            });
            if (checkedKeys.join(',') !== selectedRowKeys.value?.join(',')) {
                selectedRowKeys.value = checkedKeys;
                emit('update:selectedRowKeys', checkedKeys);
            }
            emit('checkedAll', rows);
        };

        // 点击排序执行
        const onSort = (column: ColumnProps, sortType: ColumnProps['sortType']) => {
            sortHandler(store, column, sortType);

            if (props.onSort) {
                props.onSort(column, column.sortType);
            }
        };

        // 树形展开收缩
        const onShowChildren = (row: any) => {
            showHideChildren(store, row);
        };

        // 展开自定义内容
        const onExpand = (column: ColumnProps, row: any) => {
            addRemoveExpand(store, column, row);
        };

        // resize开始
        const onDragStart = (column: ColumnProps, e: any) => {
            onResizeStart(store, column, e);

            document.addEventListener('mousemove', onDragMove, false);
            document.addEventListener('mouseup', onDragEnd, false);
        };
        // resize鼠标移动
        const onDragMove = (e: any) => {
            onResizeMove(store, e);
        };
        // resize停止
        const onDragEnd = () => {
            document.removeEventListener('mousemove', onDragMove);
            document.removeEventListener('mouseup', onDragEnd);
            onResizeEnd(store, wrap.value);
        };

        // resize 辅助线条样式
        const resizeStyle = computed(() => ({
            'display': store.resizing ? 'block' : 'none',
            'left': store.posX + 'px'
        }));

        const getAllChecked = () => {
            return store.data.filter((item: any) => {
                return item._checked;
            });
        };

        const setChecked = (id: string | number, checked: boolean) => {
            const row: any = store.data.find((item: any) => {
                item.id === id;
            });
            onRowChecked(row, checked);
        };

        const onInitColumnWidth = (index: number, width: number) => {
            store.columns[index]._width = width;
        };
        const onResizeHeader = (width: number, height: number) => {
            store.headerSize.width = width;
            store.headerSize.height = height;
        };
        const onResizeSummary = (width: number, height: number) => {
            store.summarySize.width = width;
            store.summarySize.height = height;
        };
        const onScrollBody = (scrollLeft: number, clientWidth: number, scrollWidth: number) => {
            updateScrollFixed(maxFixedLeft, minFixedRight, store, scrollLeft, clientWidth, scrollWidth);
            if (store.headerLeft !== scrollLeft) {
                store.headerLeft = scrollLeft;
            }
        };

        expose({
            clearSelect (){
                store.data
                    .filter((item: any) => item._highlight)
                    .forEach((item: any) => item._highlight = false);
            },
            checkAll (checked: boolean) {
                onHeadChecked(checked);
            },
            getAllChecked () {
                return getAllChecked();
            },
            setChecked
        });

        onMounted(() => {
            const body = wrap.value.querySelector('.cm-table-body');
            const ro = new ResizeObserver((entries) => {
                entries.forEach((entry) => {
                    observerSizeChange(store, wrap.value);
                });
            });
            ro.observe(body);
            onBeforeUnmount(() => {
                ro.unobserve(body);
            });
        });

        const style = computed(() => {
            return ({
                'max-height': props.height ? `${props.height}px` : '',
            });
        });
        const isSticky = computed(() => !!props.height);

        provide(TableContextKey, {
            store,
            onSelectRow, onRowChecked, onHeadChecked, onSort,
            onShowChildren, onExpand, onDragStart, highlight: props.highlight, border: props.border,
            spanMethod: props.spanMethod, empty: props.empty
        });
        return () => <div class={classList.value} ref={wrap}>
            <div class="cm-table-resize-helper" style={resizeStyle.value}></div>
            <div class="cm-table-loading"></div>
            {props.loading ? <Spin type="dot" title={props.loadingText || ''}></Spin> : null}
            { props.title ? <div class="cm-table-title">{props.title}</div> : null}
            <div class="cm-table" style={style.value} >
                {
                    props.showHeader
                        ? <Head data={store} sticky={isSticky.value} onInitColumnWidth={onInitColumnWidth} onResizeHeader={onResizeHeader} virtual={props.virtual}/>
                        : null
                }
                <Body data={store} onScroll={onScrollBody} height={props.height} virtual={props.virtual}/>
                {
                    props.showSummary
                        ? <Summary data={store} onResizeSummary={onResizeSummary} summaryMethod={props.summaryMethod}/>
                        : null
                }
            </div>
            {
                props.footer
                    ? <div class="cm-table-footer">{props.footer}</div>
                    : null
            }
        </div>;
    }
});
